package Tools;
import java.io.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;

import robotlion.controller.InfoIncendie;
import robotlion.controller.InfoRobot;
import robotlion.view.Observateur;

public class StatsManager implements Observateur {

    private Date startSimulationDate;
    private Date endSimulationDate;
    private HashMap<String,Integer> robotsDistances;  // Hash de paires nomRobot/distance totale parcourue
    private HashMap<String,Integer> robotsNbIncendies;  // Hash de paires nomRobot/nbIncendies éteints
    private int nombreIncendies;
    private boolean recordStarted;
    
    private String statsFilePath;
    private File statsFile;
    private FileOutputStream statsFileStream;
    private String fileName;
    
    
    // Initialise les attributs, sauve la date de début et l'écrit dans le fichier
    public StatsManager(String fileName) {
        
        robotsDistances = new HashMap<>();
        robotsNbIncendies = new HashMap<>();
        Date creationFile = new Date();
        recordStarted = false;
        Locale locale = Locale.getDefault();
        DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
        this.fileName = fileName.substring(0,fileName.lastIndexOf(".")) +"_"+dateFormat.format(creationFile).toString()+".txt";
        
        statsFile = new java.io.File(this.fileName);
        statsFilePath = null;
        try {
            statsFile.createNewFile();
            statsFilePath = statsFile.getAbsolutePath();
            
        } catch (IOException ex) {
            //Logger.getLogger(RobotLion.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public void startStats()
    {
        recordStarted = true;
        startSimulationDate = new Date();

        Locale locale = Locale.getDefault();
        DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL, locale);
        appendlnMessage("Date de la simulation : " + dateFormat.format(startSimulationDate).toString());
        appendlnMessage("");
        appendlnMessage("****** LOG ****** ");
        appendlnMessage("");
    }
    
    public void ajouterDistanceRobot(String nomRobot, int distance){
        if (robotsDistances.containsKey(nomRobot))
        {
            robotsDistances.put(nomRobot, robotsDistances.get(nomRobot) + distance);
        }
        else
        {
            robotsDistances.put(nomRobot, distance);
        }
        ajouterStat("Le robot "+nomRobot+" a parcouru "+distance+" mètres");
    }
    
    public void ajouterIncendieRobot(String nomRobot){
        if (robotsNbIncendies.containsKey(nomRobot))
        {
            robotsNbIncendies.put(nomRobot, robotsNbIncendies.get(nomRobot) + 1);
        }
        else
        {
            robotsNbIncendies.put(nomRobot, 1);
        }
        ajouterStat("Le robot "+nomRobot+" a éteint un incendie");
    }
    
    // Fonction à appeler lorsque la simulation est finie.
    // Sauve la date courante 
    public void stopStats(){
        recordStarted = false;
        endSimulationDate = new Date();
    }
    
    // Ajoute le message au fichier
    private void appendMessage(String message)
    {
        statsFileStream = null;
        String line = message;
        byte[] msg = line.getBytes();
        try 
        { 
            statsFileStream = new java.io.FileOutputStream(statsFile,true);
            statsFileStream.write(msg);
            statsFileStream.close();
        } 
        catch (FileNotFoundException notFoundEx) 
        { 
            //Logger.getLogger(RobotLion.class.getName()).log(Level.SEVERE, null, notFoundExc);
        }
        catch (java.io.IOException ex)
        {
            //Logger.getLogger(RobotLion.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    // Ajoute une ligne avec le message au fichier
    private void appendlnMessage(String message)
    {
        appendMessage("\r\n"+message);
    }
    
    // Permet d'ajouter une stat à la volée. Affiche la durée après l'éxécution 
    public void ajouterStat(String message){
        String duree = "00:00:00";
        try{
            Long debut = startSimulationDate.getTime();
            Long fin = new Date().getTime();
            Long intervalle = fin - debut;
            duree = String.format("%d:%d:%d", 
                TimeUnit.MILLISECONDS.toHours(intervalle),
                TimeUnit.MILLISECONDS.toMinutes(intervalle),
                TimeUnit.MILLISECONDS.toSeconds(intervalle)
            );
        }catch(Exception e){}
        
        appendlnMessage(duree + " " + message);
    }

    public String getStatsFilePath() {
        return statsFilePath;
    }
    
    public void saveStats(){
        appendlnMessage("");
        appendlnMessage("****** RESUME DE LA SIMULATION ****** ");
        appendlnMessage("");
        // Pour le format des dates : 
        Locale locale = Locale.getDefault();
        DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL, locale);

        String horaire = getTime(startSimulationDate);
        appendlnMessage("Heure de début de la simulation : " + horaire);
        
        horaire = getTime(endSimulationDate);
        appendlnMessage("Heure de fin de la simulation : "+ horaire);
        
        appendlnMessage("Nombre total d'incendies : " + nombreIncendies);
        
        appendlnMessage("");
        
        // pour trier le hash robotsDistances : 
        TreeMap sortedMap = new TreeMap(robotsDistances);
        Set set = sortedMap.entrySet();
        Iterator i = set.iterator();
        
        while(i.hasNext()){
            Map.Entry robot = (Map.Entry)i.next();
            appendlnMessage(robot.getKey().toString() + " : ");
            appendlnMessage("   - " +robot.getValue().toString() + " mètres parcourus");
            if (robotsNbIncendies.containsKey((String)robot.getKey())){
                if (robotsNbIncendies.get((String)robot.getKey()) == 1){
                    appendlnMessage("   - " +"1 incendie éteint");
                }else{
                    appendlnMessage("   - " +robotsNbIncendies.get((String)robot.getKey())+" incendies éteints");
                }
            }else{
                appendlnMessage("   - 0 incendie éteint");
            }
        }
        
        
        appendlnMessage("****** FIN DE LA SIMULATION ****** ");
    }
    
    public HashMap<String, Integer> getRobotsDistances() {
        return robotsDistances;
    }

    public HashMap<String, Integer> getRobotsNbIncendies() {
        return robotsNbIncendies;
    }
            
    
    public void setNombreIncendies(int nbIncendies){
        nombreIncendies = nbIncendies;
    }
    
    public void addIncendie(boolean feuPropage){
        nombreIncendies += 1;
        ajouterStat("Un nouvel incendie s'est déclaré");
    }
    
    public void addIncendie(){
        nombreIncendies += 1;
    }
    
    public void delIncendie(){
        nombreIncendies -= 1;
    }
    
    public void ajouterStatVitesseVent(int vitesse){
        ajouterStat("La vitesse du vent est passée à " + vitesse + " km/h");
    }
    
    public void ajouterStatDirectionVent(String direction){
        ajouterStat("La direction du vent est passée à " + direction);
    }
    
//    // Retourne la date et l'heure qu'il est lors de l'appel
//    private String getDateTime() {
//        DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
//        Date date = new Date();
//        return dateFormat.format(date);
//    }
    
//     Retourne l'heure correspondant à la date
    private String getTime(Date date) {
        DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
        return dateFormat.format(date);
    }

    
    @Override
    public void updateObs(Object info) {
        if(recordStarted)
        {
            if(info.getClass() == InfoRobot.class)
            {
                InfoRobot inforecup = (InfoRobot)info;
                switch(inforecup.etat)
                {
                    case SeDeplace:
                        this.ajouterDistanceRobot(inforecup.nom, inforecup.vitesse);
                        break;
                    case Libre:
                        this.ajouterIncendieRobot(inforecup.nom);
                        break;
                }
            }
        }
    }

    
}
